package cn.thoughtworks.school.programCenter.controllers;

import cn.thoughtworks.school.programCenter.annotations.Auth;
import cn.thoughtworks.school.programCenter.entities.*;
import cn.thoughtworks.school.programCenter.entities.commands.CreateUserAssignmentDurationCommand;
import cn.thoughtworks.school.programCenter.exceptions.BusinessException;
import cn.thoughtworks.school.programCenter.repositories.*;
import cn.thoughtworks.school.programCenter.services.AssignmentService;
import cn.thoughtworks.school.programCenter.services.DiffService;
import cn.thoughtworks.school.programCenter.services.QuizCenterService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

import static cn.thoughtworks.school.programCenter.entities.Operation.CREATE;
import static cn.thoughtworks.school.programCenter.entities.Operation.DELETE;
import static cn.thoughtworks.school.programCenter.entities.Operation.UPDATE;

@RestController
@RequestMapping(value = "/api/v2")
public class AssignmentController {

    @Autowired
    private AssignmentRepository assignmentRepository;
    @Autowired
    private QuizCenterService quizCenterService;
    @Autowired
    private AssignmentService assignmentService;
    @Autowired
    private AssignmentQuizRepository assignmentQuizRepository;
    @Autowired
    private TopicRepository topicRepository;
    @Autowired
    private ReviewQuizRepository reviewQuizRepository;
    @Autowired
    private TaskRepository taskRepository;
    @Autowired
    private DiffService diffService;
    @Autowired
    private UserAssignmentQuizTagRepository userAssignmentQuizTagRepository;
    private ObjectMapper oMapper = new ObjectMapper();

    @RequestMapping(value = "/assignments", method = RequestMethod.POST)
    public ResponseEntity addAssignment(@RequestBody Assignment assignment, @Auth User current) throws BusinessException {
        assignment.setCreatorId(current.getId());
        assignment.setVisible(true);
        assignment.setCreateTime(new Date());
        Assignment newAssignment = assignmentRepository.save(assignment);
        diffService.updateAssignmentNotification(assignment.getId(), current.getId(), CREATE);
        HashMap data = new HashMap();
        data.put("uri", "/api/v2/assignments/" + newAssignment.getId());
        data.put("id", newAssignment.getId());
        return new ResponseEntity<>(data, HttpStatus.CREATED);
    }

    @RequestMapping(value = "/tasks/{taskId}/assignments", method = RequestMethod.GET)
    public ResponseEntity getAssignments(@PathVariable Long taskId, @Auth User current) throws BusinessException {
        taskRepository.findByIdAndVisibleIsTrue(taskId).orElseThrow(() -> new BusinessException("task不存在"));
        List<Assignment> assignmentList = assignmentRepository.findByTaskIdAndVisibleIsTrueOrderByOrderNumber(taskId);
        List assignments = getAssignments(current.getId(), assignmentList);
        return new ResponseEntity<>(assignments, HttpStatus.OK);
    }

    @RequestMapping(value = "/tutor/students/{studentId}/tasks/{taskId}/assignments", method = RequestMethod.GET)
    public ResponseEntity tutorGetAssignments(@PathVariable Long taskId, @PathVariable Long studentId) {
        List<Assignment> assignmentList = assignmentRepository.findByTaskIdAndVisibleIsTrueOrderByOrderNumber(taskId);
        List assignments = getAssignments(studentId, assignmentList);
        return new ResponseEntity<>(assignments, HttpStatus.OK);
    }

    private List getAssignments(Long studentId, List<Assignment> assignmentList) {
        return assignmentList.stream()
            .filter((Assignment assignment) ->
                assignment.getSelectedQuizzes().size() > 0)
            .map((Assignment assignment) -> {
                int assignmentQuizCount = assignment.getSelectedQuizzes().size();
                List<ReviewQuiz> assignmentQuizList = reviewQuizRepository.findByAssignmentIdAndStudentId(assignment.getId(), studentId);
                Map map = oMapper.convertValue(assignment, Map.class);
                map.put("isStart", assignmentService.isStart(assignment.getId(), studentId));
                if (assignment.getType().equals("BASIC_QUIZ") || assignment.getType().equals("SURVEY_QUIZ")) {
                    map.put("status", assignmentQuizList.isEmpty() ? "未提交" : "已完成");
                    return map;
                }
                if (assignment.getType().equals("LOGIC_QUIZ")) {
                    map.put("status", assignmentQuizList.isEmpty()
                        ? getLogicQuizTimeOutStatus(studentId, assignment)
                        : "已完成");
                    return map;
                }
                map.put("status", "已完成 " + getFinishCount(assignmentQuizList) + " / " + assignmentQuizCount);
                map.put("isFinished", getFinishCount(assignmentQuizList) == assignmentQuizCount);
                return map;
            }).collect(Collectors.toList());
    }

    private String getLogicQuizTimeOutStatus(Long studentId, Assignment assignment) {
        return quizCenterService.isLogicQuizFinish(studentId, assignment)
            ? "已完成"
            : "未提交";

    }

    private int getFinishCount(List<ReviewQuiz> list) {
        int finished = list.stream().filter(item -> "已完成".equals(item.getStatus())).collect(Collectors.toList()).size();
        int excellent = list.stream().filter(item -> "优秀".equals(item.getStatus())).collect(Collectors.toList()).size();
        return finished + excellent;
    }

    @RequestMapping(value = "/assignments/quizzesByIds", method = RequestMethod.GET)
    public ResponseEntity getQuizzesByIds(
        @RequestParam(value = "type", defaultValue = "") String type,
        @RequestParam(value = "ids", defaultValue = "") String ids) {
        List quizzes = getQuizzesByQuizIds(ids, type);

        return new ResponseEntity<>(quizzes, HttpStatus.OK);
    }

    @RequestMapping(value = "/assignments/{assignmentId}", method = RequestMethod.GET)
    public ResponseEntity getAssignment(@PathVariable Long assignmentId) throws BusinessException {
        Assignment assignment = assignmentRepository
            .findById(assignmentId)
            .orElseThrow(() -> new BusinessException(String.format("Unknown assignment with id: %s", assignmentId)));
        return new ResponseEntity<>(assignment, HttpStatus.OK);
    }

    @RequestMapping(value = "/assignments/{assignmentId}", method = RequestMethod.DELETE)
    public ResponseEntity deleteAssignment(@PathVariable Long assignmentId,
                                           @Auth User user) throws BusinessException {
        assignmentRepository
            .findById(assignmentId)
            .orElseThrow(() -> new BusinessException(String.format("Unknown assignment with id: %s", assignmentId)));

        assignmentQuizRepository.deleteAllByAssignmentId(assignmentId);
        diffService.updateAssignmentNotification(assignmentId, user.getId(), DELETE);
        assignmentRepository.deleteById(assignmentId);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @RequestMapping(value = "/assignments/{assignmentId}", method = RequestMethod.PUT)
    public ResponseEntity updateAssignment(@PathVariable Long assignmentId,
                                           @Auth User user,
                                           @RequestBody Assignment assignmentInfo) throws BusinessException {
        Assignment assignment = assignmentRepository
            .findById(assignmentId)
            .orElseThrow(() -> new BusinessException(String.format("Unknown assignment with id: %s", assignmentId)));

        assignment.setType(assignmentInfo.getType());
        assignment.setTitle(assignmentInfo.getTitle());
        assignmentRepository.save(assignment);
        diffService.updateAssignmentNotification(assignment.getId(), user.getId(), UPDATE);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @RequestMapping(value = "/assignments/quizzes", method = RequestMethod.GET)
    public ResponseEntity getAssignmentQuizzes(@RequestParam(value = "type", defaultValue = "") String type,
                                               @PageableDefault(sort = {"id"}, direction = Sort.Direction.DESC) Pageable pageable,
                                               @RequestParam(required = false) Long quizId,
                                               @RequestParam(required = false) Long createId,
                                               @RequestParam(required = false) String description,
                                               @RequestParam(required = false) List<String> tags,
                                               @Auth User user) throws BusinessException {

        Map map = quizCenterService.getAssignmentQuizzes(type, pageable, quizId, createId, description, tags, user.getId());
        return new ResponseEntity<>(map, HttpStatus.OK);
    }

    @RequestMapping(value = "/tasks/{taskId}/assignments/{assignmentId}/quizzes", method = RequestMethod.POST)
    public ResponseEntity addAssignmentQuiz(@PathVariable Long taskId,
                                            @PathVariable Long assignmentId,
                                            @RequestBody ArrayList<Long> quizIds,
                                            @Auth User current) throws BusinessException {
        if (quizIds.size() == 0) {
            throw new BusinessException("当前未选中题目");
        }
        Assignment assignment = assignmentRepository.getOne(assignmentId);
        List<AssignmentQuiz> oldAssignmentQuizzes = assignment.getSelectedQuizzes();
        List<Long> oldQuizIds = oldAssignmentQuizzes.stream().map(AssignmentQuiz::getQuizId).collect(Collectors.toList());
        List<AssignmentQuiz> newAssignmentQuizzes = oldAssignmentQuizzes.stream().filter(item -> quizIds.indexOf(item.getQuizId()) > -1).collect(Collectors.toList());
        List<Long> increaseQuizIds = quizIds.stream().filter(item -> oldQuizIds.indexOf(item) < 0).collect(Collectors.toList());

        increaseQuizIds.forEach(quizId -> {
            AssignmentQuiz assignmentQuiz = new AssignmentQuiz();
            assignmentQuiz.setAssignmentId(assignmentId);
            assignmentQuiz.setQuizId(quizId);
            assignmentQuiz.setOrderNumber((long) (newAssignmentQuizzes.size()));
            assignmentQuiz.setCreatorId(current.getId());
            newAssignmentQuizzes.add(assignmentQuiz);
        });
        assignment.setSelectedQuizzes(newAssignmentQuizzes);
        assignmentRepository.save(assignment);
        diffService.updateAssignmentNotification(assignment.getId(), current.getId(), UPDATE);

        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    private List getQuizzesByQuizIds(String ids, String type) {
        if (ids.equals("")) {
            return new ArrayList();
        }
        if (type.equals("BASIC_QUIZ")) {
            return quizCenterService.getSelectingBasicQuizzes(ids);
        }
        if (type.equals("SURVEY_QUIZ")) {
            return quizCenterService.getSelectingSurveyQuizzes(ids);
        }
        if ("HOMEWORK_QUIZ".equals(type)) {
            return quizCenterService.getSelectHomeworkQuizzes(ids);
        }
        if ("ONLINE_CODING_QUIZ".equals(type)) {
            return quizCenterService.getSelectOnlineCodingQuizzes(ids);
        }
        if ("ONLINE_LANGUAGE_QUIZ".equals(type)) {
            return quizCenterService.getSelectOnlineLanguageQuizzes(ids);
        }
        if ("SUBJECTIVE_QUIZ".equals(type)) {
            return quizCenterService.getSelectSubjectiveQuizzes(ids);
        }
        if ("LOGIC_QUIZ".equals(type)) {
            return quizCenterService.getSelectLogicQuizzes(ids);
        }
        if ("SIMPLE_CODING_QUIZ".equals(type)) {
            return quizCenterService.getSelectSimpleCodingQuizzes(ids);
        }

        return new ArrayList();
    }

    @RequestMapping(value = "/programs/{programId}/students/{studentId}/assignments", method = RequestMethod.GET)
    public ResponseEntity getStudentsTasks(@PathVariable Long programId, @PathVariable Long studentId, @Auth User current) throws BusinessException {
        List<Topic> topics = topicRepository.findByProgramIdAndVisibleOrderByOrderNumberAsc(programId, true);
        List<Task> tasks = getAllTasks(topics);
        List<Task> currentTopicTasks;
        ArrayList data = new ArrayList();
        for (Topic topic : topics) {
            currentTopicTasks = tasks.stream()
                .filter(task -> Objects.equals(task.getTopicId(), topic.getId()))
                .collect(Collectors.toList());
            currentTopicTasks.sort(Comparator.comparing(Task::getOrderNumber));
            currentTopicTasks.stream().forEach(task -> getStatusForAssignment(task.getAssignments(), studentId, data, current.getId()));
        }

        return new ResponseEntity<>(data, HttpStatus.OK);
    }

    private void getStatusForAssignment(List<Assignment> assignments, Long studentId, ArrayList data, Long userId) {
        List<Assignment> assignmentList = assignments.stream()
            .filter(assignment -> Objects.equals(true, assignment.getVisible()))
            .filter(assignment -> !Objects.equals("LOGIC_QUIZ", assignment.getType()))
            .filter(assignment -> assignment.getSelectedQuizzes().size() > 0)
            .sorted(Comparator.comparing(Assignment::getOrderNumber))
            .collect(Collectors.toList());
        assignmentList.forEach(assignment -> {
            HashMap assignmentInfo = new HashMap();
            List<ReviewQuiz> reviewQuizzes = reviewQuizRepository.findAllByAssignmentIdAndStudentId(assignment.getId(), studentId);

            ObjectMapper oMapper = new ObjectMapper();
            Map map = oMapper.convertValue(assignment, Map.class);
            Task task = taskRepository
                .findById(assignment.getTaskId())
                .orElse(new Task());

            Topic topic = topicRepository
                .findById(task.getTopicId())
                .orElse(null);
            boolean hasAssignmentQuizTag = userAssignmentQuizTagRepository.findByAssignmentIdAndStudentIdAndOperatorId(assignment.getId(), studentId, userId).size() > 0;
            map.put("statusOld", assignmentService.getAssignmentStatusOld(assignment, reviewQuizzes));
            map.put("status", assignmentService.getAssignmentStatus(assignment, hasAssignmentQuizTag, reviewQuizzes));
            assignmentInfo.put("assignment", map);
            assignmentInfo.put("topic", topic);
            assignmentInfo.put("task", task);
            data.add(assignmentInfo);
        });
    }

    private List<Task> getAllTasks(List<Topic> topics) {
        List<Long> topicIds = topics.stream().map(Topic::getId).collect(Collectors.toList());
        return taskRepository.findByVisibleIsTrueAndTopicIdIn(topicIds);
    }

    @PutMapping("/assignments")
    public ResponseEntity dragSort(@RequestBody List<Assignment> assignments) {
        for (int i = 0; i < assignments.size(); ++i) {
            assignments.get(i).setOrderNumber((long) i);
        }

        assignmentRepository.saveAll(assignments);

        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @PutMapping("/assignments/{id}/visible")
    public ResponseEntity changeAssignmentVisible(@PathVariable Long id,
                                                  @Auth User user,
                                                  @RequestBody Map data) throws BusinessException {
        Assignment assignment = assignmentRepository.findById(id)
            .orElseThrow(() -> new BusinessException("task 不存在"));

        assignment.setVisible((Boolean) data.get("visibility"));
        assignmentRepository.save(assignment);
        diffService.updateAssignmentNotification(assignment.getId(), user.getId(), UPDATE);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @PutMapping(value = "/tasks/{taskId}/assignments/visible")
    public ResponseEntity changeAssignmentsVisible(@PathVariable Long taskId,
                                                   @Auth User user,
                                                   @RequestBody Map data) throws BusinessException {
        Task task = taskRepository.findById(taskId)
            .orElseThrow(() -> new BusinessException("task不存在"));
        Boolean visible = (boolean) data.get("visibility");
        task.getAssignments().forEach(item -> item.setVisible(visible));
        taskRepository.save(task);
        diffService.updateTaskNotification(taskId,user.getId(),UPDATE);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }


    @PostMapping("/tasks/assignments/users")
    public ResponseEntity startAssignment(@RequestBody CreateUserAssignmentDurationCommand createUserAssignmentDurationCommand) {
        assignmentService.startAssignment(createUserAssignmentDurationCommand);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }


    @GetMapping("/tasks/{taskId}/assignments/{assignmentId}")
    public ResponseEntity getUserAssignmentDuration(@PathVariable Long taskId,
                                                    @PathVariable Long assignmentId,
                                                    @Auth User user) {
        return ResponseEntity.ok(assignmentService.getUserAssignmentDuration(user.getId(), taskId, assignmentId));
    }

    @GetMapping("programs/basicQuizzes")
    public ResponseEntity getCurrentOrgProgramsBasicQuizzes(@Auth User user) {
        return ResponseEntity.ok(assignmentService.getCurrentOrgProgramsBasicQuizzes(user.getId()));
    }

    @PutMapping("programs/tasks/{taskId}/assignments/{assignmentId}/score")
    public ResponseEntity calculateBasicQuizScore(@PathVariable Long taskId,
                                                  @PathVariable Long assignmentId) {
        assignmentService.calculateBasicQuizAssignmentScore(taskId, assignmentId);
        return ResponseEntity.noContent().build();
    }


}
